{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "33d8a757-416a-4d64-9e39-a48cceec21fa",
   "metadata": {},
   "source": [
    "### 永久保存数据\n",
    "\n",
    "之前网站有个缺点，重启服务后，之前的聊天信息没有了，因此需要引入数据库保存聊天数据。\n",
    "\n",
    "### MongoDB\n",
    "\n",
    "https://www.mongodb.com/\n",
    "\n",
    "1. MongoDB是一个基于分布式文件存储的数据库。\n",
    "2. MongoDB是一个介于关系数据库和非关系数据库之间的产品，是非关系数据库当中功能最丰富，最像关系数据库的。它支持的数据结构非常松散，是类似json的bson格式"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "948f11f0-eb4c-4aad-b933-52cc8d3d4467",
   "metadata": {},
   "source": [
    "### 环境配置\n",
    "\n",
    "```\n",
    "pip3 install pymongo\n",
    "```\n",
    "\n",
    "#### 使用例子\n",
    "\n",
    "```python\n",
    "import pymongo\n",
    "#连接数据库服务器 ↓\n",
    "client = pymongo.MongoClient(\"localhost\",27017)\n",
    "#选择数据库 ↓\n",
    "db_test = client.test #推荐使用：db_test = client[\"test\"]\n",
    "#获取集合 ↓\n",
    "collection1 = db_test.col\n",
    "#获取collection1集合内全部数据 ↓\n",
    "x = collection1.find()\n",
    "for i in x:\n",
    "    print(i)#打印一条数据\n",
    "    print(i[\"name\"])#打印key=name的value值\n",
    "    print(type(i))#字典\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9698c826-4f01-4180-9f07-0c7b2968f32f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 链接数据库\n",
    "import pymongo\n",
    "client = pymongo.MongoClient(\"localhost\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "33a676e0-2b87-4bee-a827-5ac79d535108",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 插入数据\n",
    "chats = client.test.chats\n",
    "chats.insert_one({'timestamp': '23:48:35', 'from': '李娜', 'message': '大家好'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1c63c927-acee-4385-97ff-2272b8ce97df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'_id': ObjectId('62a05e7c4833e070a7354cc9'), 'timestamp': '23:48:35', 'from': '李娜', 'message': '大家好'}\n"
     ]
    }
   ],
   "source": [
    "# 查询数据\n",
    "r = chats.find({})\n",
    "for v in r:\n",
    "    print(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32388097-1673-4f0f-a4b0-0942b918d4e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "from flask import Flask, redirect, render_template, request, session\n",
    "import pymongo\n",
    "\n",
    "\n",
    "app = Flask(__name__)\n",
    "app.secret_key = \"randomstring12345\"  # 大家可以不一样的，用于生成随机ID的随机种子，类似random的seed\n",
    "client = pymongo.MongoClient(\"localhost\")\n",
    "messages = client.test.messages # 代替原来的messages，\n",
    "# messages = []\n",
    "\n",
    "\n",
    "def add_messages(username, message):\n",
    "    # 添加消息，保存在列表里面\n",
    "    now = datetime.now().strftime(\"%H:%M:%S\") # 记录时间：格式：00:00:00\n",
    "    messages_dict = {\"timestamp\": now, \"from\": username, \"message\": message} # 之前是字符串，现在换成字典保存\n",
    "    messages.insert_one(messages_dict) #保存到数据库\n",
    "    # messages.append(messages_dict)\n",
    "\n",
    "\n",
    "@app.route(\"/\", methods=[\"GET\", \"POST\"])  # 允许的方法有POST和GET，其他不写methods默认就是GET\n",
    "def index():\n",
    "    \"\"\"主页\"\"\"\n",
    "    if request.method == \"POST\":\n",
    "        # 如果是POST请求，进入这里，在请求中把username放到session里面\n",
    "        session[\"username\"] = request.form[\"username\"]\n",
    "\n",
    "    if \"username\" in session:\n",
    "        # 如果session中有username这个键值，就转跳到对应的用户页面\n",
    "        return redirect(session[\"username\"])\n",
    "    \n",
    "    # 如果没有，就展示首页网页。\n",
    "    return render_template(\"index2.html\")\n",
    "\n",
    "\n",
    "@app.route(\"/<username>\", methods=[\"GET\", \"POST\"])\n",
    "def user(username):\n",
    "    if request.method == \"POST\":\n",
    "        # 如果是提交聊天内容，则在这里处理\n",
    "        username = session[\"username\"]\n",
    "        message = request.form[\"message\"]\n",
    "        add_messages(username, message)\n",
    "        return redirect(session[\"username\"])\n",
    "\n",
    "    \"\"\"Display chat messages\"\"\"\n",
    "    \n",
    "    message_data = []\n",
    "    r = messages.find({}, {\"_id\": 0}) # 不要ID\n",
    "    for v in r:\n",
    "        message_data.append(v)\n",
    "    return render_template(\"chat3.html\", username=username,\n",
    "                           chat_messages=message_data)\n",
    "\n",
    "\n",
    "@app.route(\"/<username>/<message>\")\n",
    "def send_message(username, message):\n",
    "    \"\"\"\n",
    "    通过路由设置:/人名称/信息内容 来实现发送信息给某人\n",
    "    添加信息到列表里面，然后返回到用户页面\n",
    "    \"\"\"\n",
    "    add_messages(username, message)\n",
    "    return redirect(\"/\" + username)\n",
    "\n",
    "app.run(host=\"0.0.0.0\") # 启动服务器"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19afe0d1-3091-4eb2-adaf-585543a29d8e",
   "metadata": {},
   "source": [
    "## 优化代码\n",
    "\n",
    "1. 更新速度变慢，其实很多时候聊天框信息没有变化，不需要重新整理信息列表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "cdbca91b-528d-43ae-9147-6aeb978c3a85",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 插入数据\n",
    "import pymongo\n",
    "client = pymongo.MongoClient(\"mongodb+srv://mongo:Ab123456@cluster0.ioijm.mongodb.net/?retryWrites=true&w=majority\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "fd39e8d8-a7cf-4f86-9eb8-33450aac9707",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-06-09 09:43:04.343023\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<pymongo.results.InsertOneResult at 0x7f0118d3a640>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from datetime import datetime\n",
    "now = datetime.now()\n",
    "print(now)\n",
    "chats = client.test.chats\n",
    "chats.insert_one({'timestamp': now, 'from': 'Jay', 'message': '大家好3'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "899165ff-df42-4c0d-866e-8e6aa03b2070",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'_id': ObjectId('62a077e82fd62cf9c51b2ef4'), 'timestamp': datetime.datetime(2022, 6, 8, 18, 20, 24, 561000), 'from': 'louis', 'message': '你好'}\n",
      "{'_id': ObjectId('62a077f42fd62cf9c51b2ef5'), 'timestamp': datetime.datetime(2022, 6, 8, 18, 20, 36, 412000), 'from': 'louis', 'message': 'hi'}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "datetime.datetime(2022, 6, 8, 18, 20, 36, 412000)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查询数据\n",
    "chats = client.test.messages\n",
    "r = chats.find({})\n",
    "msg = []\n",
    "for v in r:\n",
    "    print(v)\n",
    "    msg.append(v)\n",
    "n = msg[-1][\"timestamp\"]\n",
    "n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "191eb5db-d216-4a35-a29e-97bc46ebd4df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'_id': ObjectId('62a15028858b4bfcd3c33909'), 'timestamp': datetime.datetime(2022, 6, 9, 9, 43, 4, 343000), 'from': 'Jay', 'message': '大家好3'}\n"
     ]
    }
   ],
   "source": [
    "# 查询数据 - 增加时间参数\n",
    "r = chats.find({\"timestamp\":{\"$gt\" : n}})\n",
    "for v in r:\n",
    "    print(v)\n",
    "    msg.append(v)\n",
    "\n",
    "n = msg[-1][\"timestamp\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "b14e92b6-e7d9-4988-95f6-f24abaa18faa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'_id': ObjectId('62a077e82fd62cf9c51b2ef4'),\n",
       "  'timestamp': datetime.datetime(2022, 6, 8, 18, 20, 24, 561000),\n",
       "  'from': 'louis',\n",
       "  'message': '你好'},\n",
       " {'_id': ObjectId('62a077f42fd62cf9c51b2ef5'),\n",
       "  'timestamp': datetime.datetime(2022, 6, 8, 18, 20, 36, 412000),\n",
       "  'from': 'louis',\n",
       "  'message': 'hi'},\n",
       " {'_id': ObjectId('62a14da8858b4bfcd3c33907'),\n",
       "  'timestamp': datetime.datetime(2022, 6, 9, 9, 32, 24, 192000),\n",
       "  'from': 'Jay',\n",
       "  'message': '大家好2'},\n",
       " {'_id': ObjectId('62a14fef858b4bfcd3c33908'),\n",
       "  'timestamp': datetime.datetime(2022, 6, 9, 9, 42, 7, 311000),\n",
       "  'from': 'Jay',\n",
       "  'message': '大家好2'},\n",
       " {'_id': ObjectId('62a15028858b4bfcd3c33909'),\n",
       "  'timestamp': datetime.datetime(2022, 6, 9, 9, 43, 4, 343000),\n",
       "  'from': 'Jay',\n",
       "  'message': '大家好3'}]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8874b2a3-4b7f-4eab-9dfc-edcef6870395",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " * Serving Flask app '__main__' (lazy loading)\n",
      " * Environment: production\n",
      "\u001b[31m   WARNING: This is a development server. Do not use it in a production deployment.\u001b[0m\n",
      "\u001b[2m   Use a production WSGI server instead.\u001b[0m\n",
      " * Debug mode: off\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " * Running on all addresses.\n",
      "   WARNING: This is a development server. Do not use it in a production deployment.\n",
      " * Running on http://10.0.20.12:5000/ (Press CTRL+C to quit)\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:21] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:21] \"GET /favicon.ico HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:22] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:26] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:28] \"GET /favicon.ico HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:31] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:32] \"GET /favicon.ico HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:38] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:39] \"GET /favicon.ico HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:43] \"GET /louis HTTP/1.1\" 200 -\n",
      "113.70.248.211 - - [09/Jun/2022 09:49:44] \"GET /favicon.ico HTTP/1.1\" 200 -\n"
     ]
    }
   ],
   "source": [
    "from datetime import datetime\n",
    "from flask import Flask, redirect, render_template, request, session\n",
    "import pymongo\n",
    "from datetime import datetime\n",
    "\n",
    "\n",
    "app = Flask(__name__)\n",
    "app.secret_key = \"randomstring12345\"  # 大家可以不一样的，用于生成随机ID的随机种子，类似random的seed\n",
    "client = pymongo.MongoClient(\"mongodb+srv://mongo:Ab123456@cluster0.ioijm.mongodb.net/?retryWrites=true&w=majority\")\n",
    "messages = client.test.messages # 代替原来的messages，\n",
    "message_data = []\n",
    "\n",
    "\n",
    "def add_messages(username, message):\n",
    "    # 添加消息，保存在列表里面\n",
    "    now = datetime.now()\n",
    "    #.strftime(\"%H:%M:%S\") # 记录时间：格式：00:00:00\n",
    "    messages_dict = {\"timestamp\": now, \"from\": username, \"message\": message} # 之前是字符串，现在换成字典保存\n",
    "    messages.insert_one(messages_dict) #保存到数据库\n",
    "    # messages.append(messages_dict)\n",
    "\n",
    "\n",
    "def get_all_message():\n",
    "    last_message_time = None\n",
    "    if len(message_data)>0:\n",
    "        last_message_time = message_data[-1][\"timestamp\"]\n",
    "    \n",
    "    if last_message_time is None:\n",
    "        r = messages.find({}, {\"_id\": 0}) # 不要ID\n",
    "    else:\n",
    "        r = messages.find({\"timestamp\":{\"$gt\" :last_message_time}}, {\"_id\": 0}) # 或者最后一条留言后面数据\n",
    "    for v in r:\n",
    "        message_data.append(v)\n",
    "    \n",
    "\n",
    "\n",
    "@app.route(\"/\", methods=[\"GET\", \"POST\"])  # 允许的方法有POST和GET，其他不写methods默认就是GET\n",
    "def index():\n",
    "    \"\"\"主页\"\"\"\n",
    "    if request.method == \"POST\":\n",
    "        # 如果是POST请求，进入这里，在请求中把username放到session里面\n",
    "        session[\"username\"] = request.form[\"username\"]\n",
    "\n",
    "    if \"username\" in session:\n",
    "        # 如果session中有username这个键值，就转跳到对应的用户页面\n",
    "        return redirect(session[\"username\"])\n",
    "    \n",
    "    # 如果没有，就展示首页网页。\n",
    "    return render_template(\"index2.html\")\n",
    "\n",
    "\n",
    "@app.route(\"/<username>\", methods=[\"GET\", \"POST\"])\n",
    "def user(username):\n",
    "    if request.method == \"POST\":\n",
    "        # 如果是提交聊天内容，则在这里处理\n",
    "        username = session[\"username\"]\n",
    "        message = request.form[\"message\"]\n",
    "        add_messages(username, message)\n",
    "        return redirect(session[\"username\"])\n",
    "\n",
    "    \"\"\"Display chat messages\"\"\"\n",
    "    \n",
    "    get_all_message()\n",
    "    return render_template(\"chat3.html\", username=username,\n",
    "                           chat_messages=message_data)\n",
    "\n",
    "\n",
    "@app.route(\"/<username>/<message>\")\n",
    "def send_message(username, message):\n",
    "    \"\"\"\n",
    "    通过路由设置:/人名称/信息内容 来实现发送信息给某人\n",
    "    添加信息到列表里面，然后返回到用户页面\n",
    "    \"\"\"\n",
    "    add_messages(username, message)\n",
    "    return redirect(\"/\" + username)\n",
    "\n",
    "\n",
    "app.run(host=\"0.0.0.0\") # 启动服务器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc4191ce-865c-4bd7-b757-6b8255d6c843",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
